目前,在這個Schedrag應用程式中,雖然已經能夠成功新增項目,但程式畫面上仍然顯示沒有任何todo,這是為什麼呢?
如果回去看第一個製作的應用程式,可以看到程式碼中有著兩樣東西:ChangeNotifier
與notifyListeners()
,他們便是能夠讓程式隨著資料內容更新一併更新應用程式的畫面內容的背後功臣。
今日,筆者將與讀者一同學習ChangeNotifier
的使用方式!學習資源為此官方說明。
廢話不多說,直接開始~
What is a state? 什麼是狀態?
The state of an app is everything that exists in memory when the app is running.
應用程式的狀態是執行時所有存在在記憶體中的內容。
在說明ChangeNotifier之前,先來了解Flutter是如何顯示UI吧。
大家有沒有想過Flutter是如何建立使用者介面呢?這個問題,可以用一個很簡單的公式說明:
使用者介面將會反應程式現在的狀態state,也就是說,每當應用程式狀態改變(如:旋轉螢幕角度、新增資料),會使得使用者介面也跟著改變。而這類型的UI程式設計,可以稱為Declarative Style,程式設計師所需要做的就是說明在各個狀態中UI所應該要有的長相。
Flutter的這種程式結構與其他應用程式開發工具(e.g. Android SDK, iOS UIKit)的概念不太相同,在Flutter中程式運作時會將使用者介面整個打掉重作,因為Flutter的運算速度足夠快速,甚至可以讓每一幀都如此,因此我們也不必限制自己只能修改部份UI內容。
Flutter中的狀態可以被分為兩大類:Ephemeral(短暫的) State與App State。下面將來簡單介紹一下他們是什麼。
Ephemeral State (a.k.a. UI State / Local State)
App State (a.k.a. Shared State)
ChangeNotifier
來實作。ChangeNotifier是Flutter SDK的一個基礎類別,它能夠在狀態變動時給「聽眾」傳遞「通知」,也就是說,當一個物件類別是ChangeNotifier,其他物件便能夠「訂閱subscribe」它,成為聽眾並獲取通知訊息。
以之前第一個程式為範例:
我們可以看到,MyAppState是ChangeNotifier的子類別,每當使用者得到新生成的詞組,也就是getNext()
被呼叫時,MyAppState()將會利用notifyListenters()
來通知其他widget,將詞組存入清單的toggleFavorite()
也是如此。
class MyAppState extends ChangeNotifier {
var current = WordPair.random();
void getNext() {
current = WordPair.random();
notifyListeners();
}
var favorites = <WordPair>[];
void toggleFavorite() {
if (favorites.contains(current)) {
favorites.remove(current);
} else {
favorites.add(current);
}
notifyListeners();
}
}
想要讓ChangeNotifier
能夠派上用場,它必須要有個聽眾,那麼,要如何讓其他Widget去「訂閱」ChangeNotifier成為聽眾呢?這時候我們就需要來了解ChangeNotifierProvider
。
上圖來自於StackOverflow - Why do we need ChangeNotifierProvider instead of just using a ChangeNotifier?的回答。
ChangeNotifierProvider
是一個將「訂閱ChangeNotifier」的步驟包裝成一個完整的ProviderListener協定的一個widget,它能夠知道該如何去「訂閱」、知道如何獲取「通知」,也能夠在需要的時候取消訂閱。
備註:一個ChangeNotifier需要先讓所有聽眾取消訂閱,才能正確的停止運作
現在回去看第一個程式的範例:
範例中,原本的widget MaterialApp
被包在ChangeNotifierProvider
之中,並且訂閱MyAppState
。
而在create
那行,ChangeNotifierProvider
是建立一個MyAppState
的instance,並不會另外重新建立一個新的物件(除非有必要性)。
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyAppState(),
child: MaterialApp(
title: 'Namer App',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: HomePage(),
),
);
}
}
如果想要使用多個provider,可以使用MultiProvider
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CartModel()),
Provider(create: (context) => SomeOtherClass()),
],
child: const MyApp(),
),
);
}
至於要如何在獲得通知時更新資料內容,除了之前使用的content.watch()
,還有一個名為Consumer
的widget,而這部份將留到明天的文章再來說明。
謝謝閱讀到這裡的讀者,明天除了介紹Consumer
,也會開始將ChangeNotifier
實作在Schedrag上!
如果有任何問題或想說的,都歡迎留言及email。明天會繼續加油更新的!
email: nnyjan02426@gmail.com
github | Schedrag